import assert from "assert";
import {blur, blur2} from "../src/index.js";

it("blur(values, r) returns values", () => {
  const V = [0, 0, 0, 0, 0, 0, 27, 0, 0, 0, 0, 0, 0, 0];
  assert.strictEqual(blur(V, 1), V);
  assert.deepStrictEqual(V, [0, 0, 0, 1, 3, 6, 7, 6, 3, 1, 0, 0, 0, 0]);
});

it("blur(values, r) observes the expected integer radius r", () => {
  assert.deepStrictEqual(blur([0, 0, 0, 0, 0, 0, 27, 0, 0, 0, 0, 0, 0, 0], 0.0).map(round), [0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 27.00, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000]);
  assert.deepStrictEqual(blur([0, 0, 0, 0, 0, 0, 27, 0, 0, 0, 0, 0, 0, 0], 1.0).map(round), [0.000, 0.000, 0.000, 1.000, 3.000, 6.000, 7.000, 6.000, 3.000, 1.000, 0.000, 0.000, 0.000, 0.000]);
  assert.deepStrictEqual(blur([0, 0, 0, 0, 0, 0, 27, 0, 0, 0, 0, 0, 0, 0], 2.0).map(round), [0.216, 0.648, 1.296, 2.160, 3.240, 3.888, 4.104, 3.888, 3.240, 2.160, 1.296, 0.648, 0.216, 0.000]);
  assert.deepStrictEqual(blur([0, 0, 0, 0, 0, 0, 27, 0, 0, 0, 0, 0, 0, 0], 3.0).map(round), [1.023, 1.338, 1.732, 2.204, 2.598, 2.834, 2.913, 2.834, 2.598, 2.204, 1.653, 1.181, 0.787, 0.472]);
});

it("blur(values, r) observes the expected fractional radius r", () => {
  assert.deepStrictEqual(blur([0, 0, 0, 0, 0, 0, 27, 0, 0, 0, 0, 0, 0, 0], 0.5).map(round), [0.000, 0.000, 0.000, 0.422, 2.531, 6.328, 8.438, 6.328, 2.531, 0.422, 0.000, 0.000, 0.000, 0.000]);
  assert.deepStrictEqual(blur([0, 0, 0, 0, 0, 0, 27, 0, 0, 0, 0, 0, 0, 0], 1.5).map(round), [0.053, 0.316, 0.949, 2.004, 3.322, 4.430, 4.852, 4.430, 3.322, 2.004, 0.949, 0.316, 0.053, 0.000]);
  assert.deepStrictEqual(blur([0, 0, 0, 0, 0, 0, 27, 0, 0, 0, 0, 0, 0, 0], 2.5).map(round), [0.672, 1.078, 1.609, 2.234, 2.813, 3.188, 3.313, 3.188, 2.813, 2.234, 1.594, 1.031, 0.594, 0.281]);
  assert.deepStrictEqual(blur([0, 0, 0, 0, 0, 0, 27, 0, 0, 0, 0, 0, 0, 0], 3.5).map(round), [1.266, 1.503, 1.780, 2.057, 2.294, 2.452, 2.505, 2.452, 2.294, 2.030, 1.701, 1.371, 1.081, 0.844]);
});

it("blur(values, r) repeats starting values before the window", () => {
  assert.deepStrictEqual(blur([27, 0, 0, 0, 0, 0, 0, 0], 0.0).map(round), [27.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000]);
  assert.deepStrictEqual(blur([27, 0, 0, 0, 0, 0, 0, 0], 1.0).map(round), [13.000, 9.000, 4.000, 1.000, 0.000, 0.000, 0.000, 0.000]);
  assert.deepStrictEqual(blur([27, 0, 0, 0, 0, 0, 0, 0], 2.0).map(round), [11.016, 9.072, 6.696, 4.104, 2.160, 0.864, 0.216, 0.000]);
  assert.deepStrictEqual(blur([27, 0, 0, 0, 0, 0, 0, 0], 3.0).map(round), [10.233, 8.974, 7.478, 5.825, 4.093, 2.676, 1.574, 0.787]);
});

it("blur(values, r) approximately preserves total value", () => {
  assert.strictEqual(blur([0, 0, 0, 0, 0, 0, 27, 0, 0, 0, 0, 0, 0, 0], 0.0).reduce((p, v) => p + v), 27);
  assert.strictEqual(blur([0, 0, 0, 0, 0, 0, 27, 0, 0, 0, 0, 0, 0, 0], 0.5).reduce((p, v) => p + v), 27);
  assert.strictEqual(blur([0, 0, 0, 0, 0, 0, 27, 0, 0, 0, 0, 0, 0, 0], 1.0).reduce((p, v) => p + v), 27);
  assert.strictEqual(blur([0, 0, 0, 0, 0, 0, 27, 0, 0, 0, 0, 0, 0, 0], 1.5).reduce((p, v) => p + v), 27);
  assert.strictEqual(blur([0, 0, 0, 0, 0, 0, 27, 0, 0, 0, 0, 0, 0, 0], 2.0).reduce((p, v) => p + v), 27.000000000000004);
  assert.strictEqual(blur([0, 0, 0, 0, 0, 0, 27, 0, 0, 0, 0, 0, 0, 0], 2.5).reduce((p, v) => p + v), 26.640625);
  assert.strictEqual(blur([0, 0, 0, 0, 0, 0, 27, 0, 0, 0, 0, 0, 0, 0], 3.0).reduce((p, v) => p + v), 26.370262390670547);
});

const unit = {
  width: 11,
  height: 11,
  data: [
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
  ]
};

it("blur2(data, r) modifies in-place", () => {
  const copy = copy2(unit);
  assert.strictEqual(blur2(copy, 1), copy);
});

it("data.height is redundant for blur2", () => {
  const copy = copy2(unit);
  delete copy.height;
  assert.deepStrictEqual(blur2(copy, 1).data.map(round), [
    0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000,
    0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000,
    0.000, 0.000, 0.001, 0.004, 0.008, 0.010, 0.008, 0.004, 0.001, 0.000, 0.000,
    0.000, 0.000, 0.004, 0.012, 0.025, 0.029, 0.025, 0.012, 0.004, 0.000, 0.000,
    0.000, 0.000, 0.008, 0.025, 0.049, 0.058, 0.049, 0.025, 0.008, 0.000, 0.000,
    0.000, 0.000, 0.010, 0.029, 0.058, 0.067, 0.058, 0.029, 0.010, 0.000, 0.000,
    0.000, 0.000, 0.008, 0.025, 0.049, 0.058, 0.049, 0.025, 0.008, 0.000, 0.000,
    0.000, 0.000, 0.004, 0.012, 0.025, 0.029, 0.025, 0.012, 0.004, 0.000, 0.000,
    0.000, 0.000, 0.001, 0.004, 0.008, 0.010, 0.008, 0.004, 0.001, 0.000, 0.000,
    0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000,
    0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000
  ]);
});

it("blur2(data, r) observes the expected integer radius r", () => {
  assert.deepStrictEqual(blur2(copy2(unit), 0), unit);
  assert.deepStrictEqual(blur2(copy2(unit), 1).data.map(round), [
    0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000,
    0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000,
    0.000, 0.000, 0.001, 0.004, 0.008, 0.010, 0.008, 0.004, 0.001, 0.000, 0.000,
    0.000, 0.000, 0.004, 0.012, 0.025, 0.029, 0.025, 0.012, 0.004, 0.000, 0.000,
    0.000, 0.000, 0.008, 0.025, 0.049, 0.058, 0.049, 0.025, 0.008, 0.000, 0.000,
    0.000, 0.000, 0.010, 0.029, 0.058, 0.067, 0.058, 0.029, 0.010, 0.000, 0.000,
    0.000, 0.000, 0.008, 0.025, 0.049, 0.058, 0.049, 0.025, 0.008, 0.000, 0.000,
    0.000, 0.000, 0.004, 0.012, 0.025, 0.029, 0.025, 0.012, 0.004, 0.000, 0.000,
    0.000, 0.000, 0.001, 0.004, 0.008, 0.010, 0.008, 0.004, 0.001, 0.000, 0.000,
    0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000,
    0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000
  ]);
  assert.deepStrictEqual(blur2(copy2(unit), 2).data.map(round), [
    0.001, 0.001, 0.002, 0.003, 0.003, 0.004, 0.003, 0.003, 0.002, 0.001, 0.001,
    0.001, 0.002, 0.004, 0.006, 0.007, 0.007, 0.007, 0.006, 0.004, 0.002, 0.001,
    0.002, 0.004, 0.006, 0.010, 0.012, 0.012, 0.012, 0.010, 0.006, 0.004, 0.002,
    0.003, 0.006, 0.010, 0.014, 0.017, 0.018, 0.017, 0.014, 0.010, 0.006, 0.003,
    0.003, 0.007, 0.012, 0.017, 0.021, 0.022, 0.021, 0.017, 0.012, 0.007, 0.003,
    0.004, 0.007, 0.012, 0.018, 0.022, 0.023, 0.022, 0.018, 0.012, 0.007, 0.004,
    0.003, 0.007, 0.012, 0.017, 0.021, 0.022, 0.021, 0.017, 0.012, 0.007, 0.003,
    0.003, 0.006, 0.010, 0.014, 0.017, 0.018, 0.017, 0.014, 0.010, 0.006, 0.003,
    0.002, 0.004, 0.006, 0.010, 0.012, 0.012, 0.012, 0.010, 0.006, 0.004, 0.002,
    0.001, 0.002, 0.004, 0.006, 0.007, 0.007, 0.007, 0.006, 0.004, 0.002, 0.001,
    0.001, 0.001, 0.002, 0.003, 0.003, 0.004, 0.003, 0.003, 0.002, 0.001, 0.001
  ]);
});

it("blur2(data, rx, 0) does horizontal blurring", () => {
  assert.deepStrictEqual(blur2(copy2(unit), 0, 0).data, [
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
  ]);
  assert.deepStrictEqual(blur2(copy2(unit), 1, 0).data.map(round), [
    0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000,
    0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000,
    0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000,
    0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000,
    0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000,
    0.000, 0.000, 0.037, 0.111, 0.222, 0.259, 0.222, 0.111, 0.037, 0.000, 0.000,
    0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000,
    0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000,
    0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000,
    0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000,
    0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000
  ]);
  assert.deepStrictEqual(blur2(copy2(unit), 2, 0).data.map(round), [
    0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000,
    0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000,
    0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000,
    0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000,
    0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000,
    0.024, 0.048, 0.080, 0.120, 0.144, 0.152, 0.144, 0.120, 0.080, 0.048, 0.024,
    0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000,
    0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000,
    0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000,
    0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000,
    0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000
  ]);
});

it("blur2(data, 0, ry) does vertical blurring", () => {
  assert.deepStrictEqual(blur2(copy2(unit), 0, 0).data, [
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
  ]);
  assert.deepStrictEqual(blur2(copy2(unit), 0, 1).data.map(round), [
    0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000,
    0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000,
    0.000, 0.000, 0.000, 0.000, 0.000, 0.037, 0.000, 0.000, 0.000, 0.000, 0.000,
    0.000, 0.000, 0.000, 0.000, 0.000, 0.111, 0.000, 0.000, 0.000, 0.000, 0.000,
    0.000, 0.000, 0.000, 0.000, 0.000, 0.222, 0.000, 0.000, 0.000, 0.000, 0.000,
    0.000, 0.000, 0.000, 0.000, 0.000, 0.259, 0.000, 0.000, 0.000, 0.000, 0.000,
    0.000, 0.000, 0.000, 0.000, 0.000, 0.222, 0.000, 0.000, 0.000, 0.000, 0.000,
    0.000, 0.000, 0.000, 0.000, 0.000, 0.111, 0.000, 0.000, 0.000, 0.000, 0.000,
    0.000, 0.000, 0.000, 0.000, 0.000, 0.037, 0.000, 0.000, 0.000, 0.000, 0.000,
    0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000,
    0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000
  ]);
  assert.deepStrictEqual(blur2(copy2(unit), 0, 2).data.map(round), [
    0.000, 0.000, 0.000, 0.000, 0.000, 0.024, 0.000, 0.000, 0.000, 0.000, 0.000,
    0.000, 0.000, 0.000, 0.000, 0.000, 0.048, 0.000, 0.000, 0.000, 0.000, 0.000,
    0.000, 0.000, 0.000, 0.000, 0.000, 0.080, 0.000, 0.000, 0.000, 0.000, 0.000,
    0.000, 0.000, 0.000, 0.000, 0.000, 0.120, 0.000, 0.000, 0.000, 0.000, 0.000,
    0.000, 0.000, 0.000, 0.000, 0.000, 0.144, 0.000, 0.000, 0.000, 0.000, 0.000,
    0.000, 0.000, 0.000, 0.000, 0.000, 0.152, 0.000, 0.000, 0.000, 0.000, 0.000,
    0.000, 0.000, 0.000, 0.000, 0.000, 0.144, 0.000, 0.000, 0.000, 0.000, 0.000,
    0.000, 0.000, 0.000, 0.000, 0.000, 0.120, 0.000, 0.000, 0.000, 0.000, 0.000,
    0.000, 0.000, 0.000, 0.000, 0.000, 0.080, 0.000, 0.000, 0.000, 0.000, 0.000,
    0.000, 0.000, 0.000, 0.000, 0.000, 0.048, 0.000, 0.000, 0.000, 0.000, 0.000,
    0.000, 0.000, 0.000, 0.000, 0.000, 0.024, 0.000, 0.000, 0.000, 0.000, 0.000
  ]);
});

function copy2({data, width, height}) {
  return {data: data.slice(), width, height};
}

function round(x) {
  return Math.round(x * 1000) / 1000 || 0;
}
